﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using System.Reflection;
using FlexLib.Plugins;
using FlexLib.FileTree;
using FlexLib.RegistryTree;
using PackageEditor.Properties;
using System.Threading;

namespace PackageEditor
{
	public partial class EditorForm : Form
	{
		private InstPackage package = new InstPackage();
		
		public enum WorkingModule
		{
			Admin,
			User
		}

		public EditorForm()
		{
			InitializeComponent();
			tabControl.Visible =
			makeInstallxmlToolStripMenuItem.Enabled =
				buildPackageToolStripMenuItem.Enabled =
				makeModulexmlToolStripMenuItem.Enabled =
				copyDataFrom.Enabled =
				saveToolStripMenuItem.Enabled =
				saveAsToolStripMenuItem.Enabled =
				saveToolStripButton.Enabled = false;

			EditPanelAdmin.SetPackage(package, WorkingModule.Admin);
			EditPanelUser.SetPackage(package, WorkingModule.User);
			iconImg.Image = Resources.ModuleIcon.ToBitmap();
		}

		private void ClearProperties()
		{
			AdminDLLPath.Items.Clear();
			UserDLLPath.Items.Clear();
			AdminDLLPath.Text = UserDLLPath.Text = modName.Text = dispName.Text = versionBox.Text = descBox.Text = "";
			IsResident.Checked = IsMultihost.Checked = false;
		}

		private void New()
		{
			tabControl.Visible = 
			makeInstallxmlToolStripMenuItem.Enabled =
				buildPackageToolStripMenuItem.Enabled =
				makeModulexmlToolStripMenuItem.Enabled =
				copyDataFrom.Enabled =
				saveToolStripMenuItem.Enabled =
				saveToolStripButton.Enabled =
				saveAsToolStripMenuItem.Enabled = true;

			ClearProperties();
			package = new InstPackage();
			EditPanelAdmin.SetPackage(package, WorkingModule.Admin);
			EditPanelUser.SetPackage(package, WorkingModule.User);
			EditPanelAdmin.New();
			EditPanelUser.New();
			iconImg.Image = Resources.ModuleIcon.ToBitmap();
			savePath = "";
			currentProjectName = null;
			this.Text = "Package Editor";
		}

		private string currentProjectName = null;

		private void newToolStripMenuItem_Click(object sender, EventArgs e)
		{
			New();
		}	

		private void ImportDLLFiles()
		{
			UserDLLPath.Items.Clear();
			AdminDLLPath.Items.Clear();
			foreach (string key in package.AdminDllFiles.Keys)
				AdminDLLPath.Items.Add(key);
			foreach (string key in package.UserDllFiles.Keys)
				UserDLLPath.Items.Add(key);
		}

		private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
		{
			switch (tabControl.SelectedIndex)
			{
				case 0:
					copyDataFrom.Enabled = true;
					copyDataFrom.Text = "Copy data from admin";
					break;
				case 1:
					copyDataFrom.Enabled = true;
					copyDataFrom.Text = "Copy data from user";
					break;
				case 2:
					copyDataFrom.Enabled = false;
					ImportDLLFiles();
					/*
					if (AdminDLLPath.SelectedItem == null && AdminDLLPath.Items.Count > 0)
						AdminDLLPath.SelectedItem = AdminDLLPath.Items[0];

					if (UserDLLPath.SelectedItem == null && UserDLLPath.Items.Count > 0)
						UserDLLPath.SelectedItem = UserDLLPath.Items[0];*/
					break;
			}
		}

		private void newToolStripButton_Click(object sender, EventArgs e)
		{
			New();
		}

		private string savePath = "";		

		private void Save(string filename)
		{
			WritePluginInfoToPackage(package);
			XmlTextWriter xmlwriter = new XmlTextWriter(filename, Encoding.UTF8);
			XmlPluginSerialization.GenerateFprojFile(xmlwriter, package, savePath);
		}

		private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SaveFileDialog saveDialog = new SaveFileDialog();
			saveDialog.AddExtension = true;
			saveDialog.Filter = "Flexmin plugin projects (*.fproj)|*.fproj|All files (*.*)|*.*";
			saveDialog.RestoreDirectory = true;
			if (saveDialog.ShowDialog() == DialogResult.OK)
			{
				savePath = Path.GetDirectoryName(saveDialog.FileName);
				Save(saveDialog.FileName);
				this.currentProjectName = saveDialog.FileName;
				this.Text = "Package Editor (" + Path.GetFileName(this.currentProjectName) + ")";
			}
		}

		private void saveToolStripButton_Click(object sender, EventArgs e)
		{
			if (currentProjectName != null)
				Save(currentProjectName);
			else
				saveAsToolStripMenuItem_Click(sender, e);
		}

		private void Open(string filename)
		{
			tabControl.Visible =
			makeInstallxmlToolStripMenuItem.Enabled =
				buildPackageToolStripMenuItem.Enabled =
				makeModulexmlToolStripMenuItem.Enabled =
				copyDataFrom.Enabled =
				saveToolStripMenuItem.Enabled =
				saveToolStripButton.Enabled =
				saveAsToolStripMenuItem.Enabled = true;

			ClearProperties();
			EditPanelAdmin.Clear();
			EditPanelUser.Clear();

			package = XmlPluginSerialization.GetPackageFromFproj(filename);
			SetPackage(package);
			
			EditPanelAdmin.SetPackage(package, WorkingModule.Admin);
			EditPanelUser.SetPackage(package, WorkingModule.User);			
		}

		private void SetFiles(FileTreeNode node, WorkingModule module)
		{
			
			switch (module)
			{
				case WorkingModule.Admin:
					EditPanelAdmin.FilesTree.SelectedNode.Tag = node;
					foreach (FileTreeNode subNode in node.SubNodes)
					{
						TreeNode newNode = EditPanelAdmin.FilesTree.SelectedNode.Nodes.Add(subNode.Name);
						newNode.Tag = subNode;
						EditPanelAdmin.FilesTree.SelectedNode = newNode.Parent;
						if (subNode.SubNodes.Count > 0)
						{
							EditPanelAdmin.FilesTree.SelectedNode = newNode;
							SetFiles(subNode, module);
							EditPanelAdmin.FilesTree.SelectedNode = newNode.Parent;
						}
					}
					break;
				case WorkingModule.User:
					EditPanelUser.FilesTree.SelectedNode.Tag = node;
					foreach (FileTreeNode subNode in node.SubNodes)
					{
						TreeNode newNode = EditPanelUser.FilesTree.SelectedNode.Nodes.Add(subNode.Name);
						newNode.Tag = subNode;
						if (subNode.SubNodes.Count > 0)
						{
							EditPanelUser.FilesTree.SelectedNode = newNode;
							SetFiles(subNode, module);
							EditPanelUser.FilesTree.SelectedNode = newNode.Parent;
						}
					}					
					break;
			}
		}

		private void SetKeys(RegTreeKey key, WorkingModule module)
		{
			switch (module)
			{
				case WorkingModule.Admin:
					EditPanelAdmin.RegistryTree.SelectedNode.Tag = key;
					foreach (RegTreeKey subKey in key.SubKeys)
					{
						TreeNode newNode = EditPanelAdmin.RegistryTree.SelectedNode.Nodes.Add(subKey.Name);
						newNode.Tag = subKey;
						EditPanelAdmin.RegistryTree.SelectedNode = newNode.Parent;
						if (subKey.SubKeys.Count > 0)
						{
							EditPanelAdmin.RegistryTree.SelectedNode = newNode;
							SetKeys(subKey, module);
							EditPanelAdmin.RegistryTree.SelectedNode = newNode.Parent;
						}
					}
					break;
				case WorkingModule.User:
					EditPanelUser.RegistryTree.SelectedNode.Tag = key;
					foreach (RegTreeKey subKey in key.SubKeys)
					{
						TreeNode newNode = EditPanelUser.RegistryTree.SelectedNode.Nodes.Add(subKey.Name);
						newNode.Tag = subKey;
						EditPanelUser.RegistryTree.SelectedNode = newNode.Parent;
						if (subKey.SubKeys.Count > 0)
						{
							EditPanelUser.RegistryTree.SelectedNode = newNode;
							SetKeys(subKey, module);
							EditPanelUser.RegistryTree.SelectedNode = newNode.Parent;
						}
					}
					break;
			}
		}

		private void SetProperties(InstPackage package)
		{
			foreach (string dllKey in package.AdminDllFiles.Keys)
				AdminDLLPath.Items.Add(dllKey);
			foreach (string dllKey in package.UserDllFiles.Keys)
				UserDLLPath.Items.Add(dllKey);
			
			modName.Text = package.ModuleName;
			dispName.Text = package.DisplayName;
			descBox.Text = package.Description;
			IsResident.Checked = package.IsResident;
			IsMultihost.Checked = package.IsMultihost;

			for (int i = 0; i < AdminDLLPath.Items.Count; i++)
				if (AdminDLLPath.Items[i].ToString() == package.AdminDllPath)
					AdminDLLPath.SelectedIndex = i;

			for (int i = 0; i < UserDLLPath.Items.Count; i++)
				if (UserDLLPath.Items[i].ToString() == package.UserDllPath)
					UserDLLPath.SelectedIndex = i;
			
			try
			{
				iconImg.Image = new Icon(package.Icon).ToBitmap();
			}
			catch { }
		}

		private void SetPackage(InstPackage package)
		{
			EditPanelAdmin.AddRootNodes();
			EditPanelUser.AddRootNodes();

			EditPanelAdmin.FilesTree.SelectedNode = EditPanelAdmin.FilesTree.Nodes[0];			
			EditPanelUser.FilesTree.SelectedNode = EditPanelUser.FilesTree.Nodes[0];
			SetFiles(package.RootNodeAdmin, WorkingModule.Admin);
			SetFiles(package.RootNodeUser, WorkingModule.User);

			EditPanelAdmin.RegistryTree.SelectedNode = EditPanelAdmin.RegistryTree.Nodes[0];
			EditPanelUser.RegistryTree.SelectedNode = EditPanelUser.RegistryTree.Nodes[0];
			SetKeys(package.RootKeyAdmin, WorkingModule.Admin);
			SetKeys(package.RootKeyUser, WorkingModule.User);

			SetProperties(package);
		}

		string currentProjectPath = null;

		// TODO: надо буит перед открытием спрашивать, не надо ли изменения сохранить - думаю тупо надо флаг юзать
		private void openToolStripMenuItem_Click(object sender, EventArgs e)
		{
			OpenFileDialog openDialog = new OpenFileDialog();
			openDialog.Filter = "Flexmin plugin projects (*.fproj)|*.fproj|All files (*.*)|*.*";
			openDialog.Multiselect = false;
			if (openDialog.ShowDialog() == DialogResult.OK)
			{
				currentProjectPath = Path.GetDirectoryName(openDialog.FileName);
				Open(openDialog.FileName);				
				this.currentProjectName = openDialog.FileName;
				this.Text = "Package Editor (" + Path.GetFileName(this.currentProjectName) + ")";		
			}
		}

		private bool CheckAllFields()
		{
			bool ret = true;
			if (AdminDLLPath.Text == "")
			{
				AdminDLLPath.BackColor = Color.Pink;
				ret = false;
			}
			if (UserDLLPath.Text == "")
			{
				UserDLLPath.BackColor = Color.Pink;
				ret = false;
			}
			if (modName.Text == "")
			{
				modName.BackColor = Color.Pink;
				ret = false;
			}
			if (dispName.Text == "")
			{
				dispName.BackColor = Color.Pink;
				ret = false;
			}
			if (versionBox.Text == "")
			{
				versionBox.BackColor = Color.Pink;
				ret = false;
			}			
			return ret;
		}

		private void buildPackageToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ImportDLLFiles();

			WritePluginInfoToPackage(package);

			BuildProgressForm buildForm = new BuildProgressForm();		

			if (CheckAllFields())
			{
				Thread buildThread = new Thread(new ParameterizedThreadStart(package.Build));
				buildThread.Start(new InstPackage.LogProgressHandler(buildForm.DoProgress));
				buildForm.ShowDialog();
			}
			else
			{
				buildForm.DoProgress(0, "Error: Not all project info filled");				
				buildForm.ShowDialog();
				tabControl.SelectedIndex = 2;
			}
		}

		private void Fields_TextChanged(object sender, EventArgs e)
		{
			TextBox box = sender as TextBox;
			if (box != null)
			{
				if (box.Text.Length > 0)
					box.BackColor = SystemColors.Window;
				else
					box.BackColor = Color.Pink;
				return;
			}
			
			ComboBox cbox = sender as ComboBox;
			if (cbox != null)
			{
				if (cbox.Text.Length > 0)
					cbox.BackColor = SystemColors.Window;
				else
					cbox.BackColor = Color.Pink;
				return;
			}
		}

		private void IconChangeButton_Click(object sender, EventArgs e)
		{
			OpenFileDialog openFileDialog = new OpenFileDialog();
			openFileDialog.Filter = "Icon files (*.ico)|*.ico";			
			openFileDialog.RestoreDirectory = true;
			openFileDialog.Multiselect = false;
			if (openFileDialog.ShowDialog() == DialogResult.OK)
			{
				string filename = openFileDialog.FileName;
				package.Icon = filename;
				iconImg.Image = new Icon(filename).ToBitmap();
			}
		}

		private void AdminDLLPath_SelectedIndexChanged(object sender, EventArgs e)
		{
			ComboBox box = sender as ComboBox;
			Fields_TextChanged(sender, e);
			
			AssemblyName assemblyName;
			try
			{
				string dllPath = package.AdminDllFiles[box.Text];
				if (!Path.IsPathRooted(dllPath))
					dllPath = currentProjectPath + "\\" + dllPath;

				assemblyName = AssemblyName.GetAssemblyName(dllPath);
			}
			catch { return; }

			versionBox.Text = assemblyName.Version.ToString();
			modName.Text = assemblyName.Name;
		}

		private void UserDLLPath_SelectedIndexChanged(object sender, EventArgs e)
		{
			ComboBox box = sender as ComboBox;
			Fields_TextChanged(sender, e);
				
			AssemblyName assemblyName;
			try
			{
				string dllPath = package.UserDllFiles[box.Text];
				if (!Path.IsPathRooted(dllPath))
					dllPath = currentProjectPath + "\\" + dllPath;
					
				assemblyName = AssemblyName.GetAssemblyName(dllPath);
			}
			catch { return; }

			versionBox.Text = assemblyName.Version.ToString();
			modName.Text = assemblyName.Name;
		}

		private void WritePluginInfoToPackage(InstPackage package)
		{
			package.ModuleName = modName.Text;
			package.DisplayName = dispName.Text;
			package.Description = descBox.Text;
			package.IsMultihost = IsMultihost.Checked;
			package.IsResident = IsResident.Checked;
			try
			{
				package.AdminDllPath = this.AdminDLLPath.Text;
			}
			catch { }
			try
			{
				package.UserDllPath = this.UserDLLPath.Text;
			}
			catch { }
		}		
	}
}
